<?php
declare (strict_types = 1);
namespace app\common\model\user;

use app\admin\traits\GetTableData;
use app\common\components\IdentityInterface;
use app\common\model\message\Comment;
use app\common\model\message\Like;
use app\common\utils\facade\EncryptUtil;
use app\common\utils\facade\FileUtil;
use think\Exception;
use think\helper\Str;
use think\Model;
use app\common\model\content\Category;
use app\common\model\content\Topic;
use app\common\model\content\Article;
use app\common\model\message\Message;

class User extends Model implements IdentityInterface
{
    use GetTableData;

    //图片上传字段与目录
    const UP_FIELD = 'avatar';
    const UP_FOLDER = 'avatar';

    //reset password token 时效
    const RESET_PASSWORD_TOKEN_EXPIRE = 60*60;

    // 设置字段信息
    protected $schema = [
        'username'              => 'string',
        'email'                 => 'string',
        'password_hash'         => 'string',
        'auth_key'              => 'string',
        'password_reset_token'  => 'string',
        'mobile'                => 'string',
        'nickname'              => 'string',
        'avatar'                => 'string',
        'status'                => 'int',
        'last_time'             => 'int',
        'create_time'           => 'int',
        'update_time'           => 'int',
    ];

    // 类型转换
    protected $type = [
        'status'                => 'integer',
        'last_time'             => 'integer',
    ];

    //关联分类
    public function categorys()
    {
        return $this->hasMany(Category::class, 'user_id', 'id');
    }
    //关联角色
    public function roles()
    {
        return $this->belongsToMany(Permission::class, Assignment::class);
    }
    //关联话题
    public function topics()
    {
        return $this->hasMany(Topic::class, 'user_id', 'id');
    }
    //关联文章
    public function articles()
    {
        return $this->hasMany(Article::class,'user_id', 'id');
    }
    //关联评论
    public function comments()
    {
        return $this->hasMany(Comment::class,'user_id', 'id');
    }
    //关联评论
    public function likes()
    {
        return $this->hasMany(Like::class,'user_id', 'id');
    }

    //(页面显示图片)
    public function getAvatarUrlAttr()
    {
        return empty($this->getAttr('avatar')) ? config('app.default_avatar') : showImg($this->getAttr('avatar'));
    }

    //获取用户名或昵称
    public function getShowNameAttr(){
        return $this->nickname ?: $this->username;
    }

    //保存之前
    public static function onBeforeWrite(Model $model)
    {
        //修改(如果修改了图片 就先删除原有图片
        //删除图片
        if ($model->isExists() && array_key_exists('avatar', $model->getChangedData())){
            $real_file = static::getRealPath($model->getOrigin('avatar'));
            if(is_file($real_file))
                FileUtil::unlinkFile($real_file);
        }

        //移动到永久目录
        if(!empty($model->avatar) && ( !$model->isExists() || array_key_exists('avatar', $model->getChangedData()))){
            $ori_img = static::getTmpPath($model->avatar);
            $des_img = static::getRealPath($model->avatar);

            if(is_file($ori_img)){
                if ( !FileUtil::moveFile($ori_img, $des_img) )
                    return false;
            }
        }
        return true;
    }
    //删除之前
    public static function onBeforeDelete(Model $model)
    {
        if(Article::where(['user_id'=>$model->id])->count()){
            throw new Exception('请先删除用户文章');
        }

        return true;
    }

    //删除之后
    public static function onAfterDelete(Model $model)
    {
        //删除图片
        if(!empty($model->avatar)) FileUtil::unlinkFile(static::getRealPath($model->avatar));
        Assignment::where(['user_id'=>$model->id])->delete();//删除指派数据(有外键的用不上)

        //删除评论
        Comment::destroy(function($query) use ($model){
            $query->where('user_id','=',$model->id);
        });

        //删除收藏
        Like::destroy(function($query) use ($model){
            $query->where('user_id','=',$model->id);
        });

        //删除留言
        Message::destroy(function($query) use ($model){
            $query->where('user_id','=',$model->id);
        });

        return true;
    }

    //获取临时文件的绝对路径
    private static function getTmpPath($file){
        return config('filesystem.disks.local.root') . '/'. $file;
    }
    //获取永久文件的绝对路径
    private static function getRealPath($file){
        return config('filesystem.disks.public.root') .'/'. $file;
    }

    //获取列表数据
    public static function getListData(array $ruleWhere=[]){
        //要查询的字段
        $fields = [
            'fields' =>['id','username','email','mobile','nickname','avatar','status','last_time','create_time','update_time'],
            'with' => [],
            'withCount' => ['articles'],
        ];

        //(构建查询条件)
        $where = [];
        if (input('start_time')){
            $where[] = ['create_time', '>=', strtotime(input('start_time'))];
        }
        if (input('end_time')){
            $where[] = ['create_time', '<=', strtotime( input('end_time'))];
        }
        if (input('name')){
            $where[] = ['username|nickname','like','%'.input('name').'%'];
        }

        //排序
        $order = ['id'=>'desc','create_time'=>'desc'];

        //[count:查询到的数目, list:记录列表]
        return static::operateDatum($where, $fields, $order, $ruleWhere);
    }

    //保存角色信息
    public function saveRoles($roles){
        //先删除该用户的所有角色
        Assignment::destroy(function($query){
            $query->where('user_id','=',$this->id);
        });

        //再添加授权信息
        if (!empty($roles))
            $this->roles()->saveAll($roles);
        return true;
    }

    //设置密码
    public static function generatePasswordHash(string $password){
        return EncryptUtil::makePasswordHash($password);
    }
    //效验密码
    public function validatePassword(string $password){
        return EncryptUtil::checkPassword($password, $this->getAttr('password_hash'));
    }
    //生成auth_key
    public static function generateAuthKey(){
        return Str::random(32);
    }
    //获取auth_key
    public function getAuthKey(){
        return $this->getAttr('auth_key');
    }
    //效验auth_key
    public function validateAuthKey($authKey){
        return $this->getAttr('auth_key') === $authKey;
    }

    //生成resetPasswordToken
    public function generateResetPasswordToken(){
        $this->setAttr('password_reset_token',static::generateString() .'_'. time());
        return $this->save();
    }

    //获取reset password token
    public function getResetPasswordTokenCrypt(){
        return EncryptUtil::lock_url($this->getAttr('password_reset_token'), config('service.user.encryptSlat'));
    }


    //根据reset password token 密文 获取用户实例 失败返回null
    public static function getIdentityByResetPasswordTokenCrypt(string $crypt){
        if (empty($crypt)) {
            return null;
        }

        //得到token明文
        $plain = EncryptUtil::unlock_url($crypt, config('service.user.encryptSlat'));
        if ( !strrpos($plain,'_') ) return null;

        //验证时间
        $time = (int) ltrim(strrchr($plain,'_'),'_');
        if($time + static::RESET_PASSWORD_TOKEN_EXPIRE < time()) return null;
        //返回用户实例
        return static::where(['password_reset_token'=>$plain])->find();
    }


    //生成随机数
    public static function generateString($length = 32){
        return Str::random($length);
    }









    /**
     * Finds an identity by the given ID.
     */
    public static function findIdentity($id){
        return static::find($id);
    }

    public static function findIdentityByUsernameAndEmail(string $username, string $email){
        return static::where(['username'=>$username, 'email'=>$email])->find();
    }

    /**
     * Finds an identity by the given token.
     */
    public static function findIdentityByAccessToken($token, $type = null){}

    /**
     * Returns an ID that can uniquely identify a user identity.
     */
    public function getId(){
        return $this->id;
    }


    /**
     * 登录之前
     */
    public function beforeLogin():bool{

        return true;
    }

    /**
     * 登录之后
     */
    public function afterLogin(){
        $this->save([
            'last_time' => time()
        ]);
    }

    /**
     * 退出之前
     */
    public function beforeLogout():bool{

        return true;
    }

    /**
     * 退出之后
     */
    public function afterLogout(){

    }

}
